from nicegui import ui, app
import asyncio
import datetime
import os

# 配置
API_BASE = "http://localhost:8000"
VIDEO_WS_URL = "ws://localhost:8000/ws/video"
DEFAULT_VIDEO_PATH = r"C:\Users\admin\Downloads\1.mp4"

# 全局状态
robot_status = {"x": 0, "y": 0, "z": 0}
video_ws = None
video_container = None
position_label = None


@ui.page('/')
async def index():
    global position_label

    # 初始化用户存储
    if 'video_source' not in app.storage.user:
        if os.path.exists(DEFAULT_VIDEO_PATH):
            # 使用app.add_static_file确保视频文件可访问[5](@ref)
            app.storage.user['video_source'] = app.add_static_file(local_file=DEFAULT_VIDEO_PATH)
            app.storage.user['video_source'] = DEFAULT_VIDEO_PATH
            app.storage.user['auto_play'] = True
        else:
            app.storage.user['video_source'] = ""
            ui.notify(f"默认视频文件 {DEFAULT_VIDEO_PATH} 不存在", type='warning')

    # 主界面布局
    with ui.grid(columns=2).classes("w-full h-screen gap-4"):
        # 第一行：视频区域（占满两列）
        with ui.grid(columns=2).classes("col-span-2 h-[35vh] gap-4"):
            # 实时视频流区域
            with ui.card().classes("h-full overflow-hidden"):
                ui.label("实时视频流").classes("text-xl")

                # 添加FLV.js库
                ui.add_body_html('''
                    <script src="https://cdn.jsdelivr.net/npm/flv.js@latest"></script>
                    <style>
                        #flv-container { width:100%; height:100% }
                        #flv-player { width:100%; height:100%; object-fit: contain }
                    </style>
                ''')

                # 视频容器
                video_container = ui.html('''
                    <div id="flv-container">
                        <video id="flv-player" controls muted></video>
                    </div>
                ''').classes("w-full h-[calc(100%-40px)]")

                # 带错误处理的播放器初始化
                ui.run_javascript('''
                    function initPlayer() {
                        const videoElement = document.getElementById('flv-player');

                        if (flvjs.isSupported()) {
                            const flvPlayer = flvjs.createPlayer({
                                type: 'flv',
                                url: 'http://127.0.0.1/live/test.live.flv',
                                isLive: true,
                                stashInitialSize: 128,
                                enableWorker: true,
                                lazyLoadMaxDuration: 3 * 60,
                                seekType: 'range'
                            });

                            flvPlayer.attachMediaElement(videoElement);
                            flvPlayer.load();

                            // 自动播放处理（需要用户交互后生效）
                            const playPromise = videoElement.play();
                            if (playPromise !== undefined) {
                                playPromise.catch(e => {
                                    console.log('自动播放被阻止，需要用户交互');
                                });
                            }

                            // 错误处理
                            flvPlayer.on(flvjs.Events.ERROR, (errType, errDetail) => {
                                console.error('播放错误:', errType, errDetail);
                                if(errType === flvjs.ErrorTypes.NETWORK_ERROR) {
                                    setTimeout(() => {
                                        flvPlayer.unload();
                                        flvPlayer.detachMediaElement();
                                        flvPlayer.attachMediaElement(videoElement);
                                        flvPlayer.load();
                                        videoElement.play();
                                    }, 3000);
                                }
                            });

                            window.flvPlayer = flvPlayer; // 保存引用
                        } else {
                            console.error('浏览器不支持FLV播放');
                            videoElement.innerHTML = '您的浏览器不支持视频播放';
                        }
                    }

                    // 延迟初始化避免加载问题
                    setTimeout(initPlayer, 500);
                ''')

            # with ui.card().classes("h-full overflow-hidden"):
            #     ui.label("实时视频流").classes("text-xl")
            #     video_container = ui.image("").classes("w-full h-[calc(100%-40px)]")

            # 视频播放器区域（优化自动播放）
            with ui.card().classes("h-full overflow-hidden"):
                ui.label("视频播放器").classes("text-xl")
                video_player = ui.video("").classes("w-full h-[calc(100%-40px)]").props("controls autoplay muted")

                # 确保自动加载和播放[2,4](@ref)
                if app.storage.user['video_source']:
                    print("1",f"{app.storage.user['video_source']}")
                    # video_player.set_source(app.storage.user['video_source'])
                    video_player.set_source(DEFAULT_VIDEO_PATH)
                    ui.run_javascript(f"""
                        const video = document.getElementById('{video_player.id}');
                        video.muted = true;
                        function ensurePlay() {{
                            if (video.readyState > 0) {{
                                video.play().catch(e => console.log('Play attempt:', e));
                            }} else {{
                                setTimeout(ensurePlay, 100);
                            }}
                        }}
                        video.addEventListener('loadedmetadata', ensurePlay);
                        video.addEventListener('canplay', ensurePlay);
                        ensurePlay();
                    """)

                # 控制按钮区域
                with ui.row().classes("w-full items-center gap-2 mt-2"):
                    ui.button("播放", on_click=video_player.play).props("flat")
                    ui.button("暂停", on_click=video_player.pause).props("flat")
                    ui.button("停止", on_click=lambda: (
                        video_player.pause(),
                        video_player.seek(0),
                        ui.notify("视频已停止")
                    )).props("flat")

                    slider = ui.slider(min=0, max=100, value=0).classes("flex-grow")
                    ui.run_javascript(f"""
                        document.getElementById('{video_player.id}').addEventListener('timeupdate', function() {{
                            const slider = document.getElementById('{slider.id}');
                            slider.value = (this.currentTime / this.duration) * 100 || 0;
                        }});
                        document.getElementById('{slider.id}').addEventListener('input', function() {{
                            const video = document.getElementById('{video_player.id}');
                            if (video.duration) {{
                                video.currentTime = (this.value / 100) * video.duration;
                            }}
                        }});
                    """)

                # 上传控件
                def handle_upload(e):
                    try:
                        timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
                        temp_file = f"upload_{timestamp}.mp4"
                        with open(temp_file, "wb") as f:
                            f.write(e.content.read())
                        app.storage.user['video_source'] = app.add_static_file(temp_file)
                        video_player.set_source(app.storage.user['video_source'])
                        ui.notify("视频上传成功！", type='positive')
                    except Exception as ex:
                        ui.notify(f"上传失败: {str(ex)}", type='negative')

                ui.upload(
                    label="上传视频文件",
                    on_upload=handle_upload,
                    auto_upload=True
                ).props("accept=.mp4,.webm,.ogg multiple=false").classes("w-full mt-2")

        # 第二行：控制面板（左）和语音输入（右）
        with ui.card().classes("h-[20vh] overflow-auto"):
            ui.label("机器人控制").classes("text-xl")
            position_label = ui.label("Position: (0, 0, 0)").classes("text-lg")
            with ui.grid(columns=3).classes("w-full gap-4"):
                for direction in ["forward", "backward", "left", "right", "up", "down"]:
                    ui.button(
                        direction.capitalize(),
                        on_click=lambda d=direction: move_robot(d)
                    ).classes("w-full")

        with ui.card().classes("h-[20vh] overflow-auto"):
            ui.label("语音控制").classes("text-xl")
            voice_input = ui.input('语音指令', placeholder='点击话筒说话').classes("w-full")
            with ui.row().classes("w-full justify-center items-center gap-2"):
                ui.button(icon='mic', on_click=lambda: ui.run_javascript(f"""
                    const input = document.getElementById('{voice_input.id}');
                    if (!('webkitSpeechRecognition' in window)) {{
                        alert('您的浏览器不支持语音输入！');
                        return;
                    }}
                    const recognition = new webkitSpeechRecognition();
                    recognition.lang = 'zh-CN';
                    recognition.start();
                    recognition.onresult = function(e) {{
                        input.value = e.results[0][0].transcript;
                    }};
                """)).props('flat color=primary').tooltip("开始语音输入")
                ui.button("清除", on_click=lambda: voice_input.set_value("")).props('flat')

        # 第三行：3D场景（调整高度为45vh）
        with ui.card().classes("col-span-2 h-[45vh] overflow-hidden"):
            ui.label("机器人3D空间").classes("text-xl")
            ui.add_body_html('''
                <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
                <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.min.js"></script>
            ''')
            scene_container = ui.html('''
                <div id="threejs-container" style="width:100%; height:100%; position:relative;">
                    <canvas id="threejs-canvas" style="display:block; width:100%; height:100%"></canvas>
                </div>
            ''').classes("w-full h-full")

            ui.run_javascript('''
                // 初始化Three.js场景
                const container = document.getElementById('threejs-container');
                const canvas = document.getElementById('threejs-canvas');
                canvas.width = container.clientWidth;
                canvas.height = container.clientHeight;
                const scene = new THREE.Scene();
                scene.background = new THREE.Color(0xf0f0f0);
                const camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000);
                camera.position.set(5, 5, 5);
                camera.lookAt(0, 0, 0);
                const renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
                renderer.setSize(canvas.width, canvas.height);
                const axesHelper = new THREE.AxesHelper(5);
                scene.add(axesHelper);
                const gridHelper = new THREE.GridHelper(10, 10);
                scene.add(gridHelper);
                const ambientLight = new THREE.AmbientLight(0x404040);
                scene.add(ambientLight);
                const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
                directionalLight.position.set(1, 1, 1);
                scene.add(directionalLight);
                const robotGeometry = new THREE.BoxGeometry(1, 1, 1);
                const robotMaterial = new THREE.MeshPhongMaterial({ color: 0x00aaff });
                const robot = new THREE.Mesh(robotGeometry, robotMaterial);
                scene.add(robot);
                const controls = new THREE.OrbitControls(camera, renderer.domElement);
                controls.enableDamping = true;
                controls.dampingFactor = 0.05;
                function animate() {
                    requestAnimationFrame(animate);
                    controls.update();
                    renderer.render(scene, camera);
                }
                animate();
                window.robotScene = {
                    scene: scene,
                    robot: robot,
                    camera: camera,
                    renderer: renderer
                };
                window.addEventListener('resize', () => {
                    camera.aspect = container.clientWidth / container.clientHeight;
                    camera.updateProjectionMatrix();
                    renderer.setSize(container.clientWidth, container.clientHeight);
                });
            ''')

            def update_robot_position():
                ui.run_javascript(f'''
                    if (window.robotScene) {{
                        window.robotScene.robot.position.set(
                            {robot_status['x']}, 
                            {robot_status['y']}, 
                            {robot_status['z']}
                        );
                    }}
                ''')

            ui.timer(0.1, update_robot_position)

    # 初始化视频流（增加超时处理）[6,7](@ref)
    await connect_video_stream()
    ui.timer(1.0, update_status)


async def connect_video_stream():
    global video_ws, video_container
    try:
        video_ws = await ui.run_javascript(f"""
            let socket = new WebSocket('{VIDEO_WS_URL}');
            socket.onopen = function() {{
                setInterval(() => socket.send('ping'), 15000); // 15秒心跳
            }};
            return socket;
        """, timeout=5.0)

        def handle_message(e):
            if video_container:
                ui.run_javascript(f"""
                    document.getElementById('{video_container.id}').src = 'data:image/jpg;base64,{e.data}'
                """)

        await ui.run_javascript(f"""
            window.videoSocket = {video_ws};
            window.videoSocket.onmessage = function(e) {{
                {handle_message.__code__.co_code}
            }};
        """)
    except Exception as e:
        print(f"连接视频流失败: {e}")


async def update_status():
    while True:
        try:
            response = await ui.run_javascript(f"""
                fetch('{API_BASE}/api/status').then(r => r.json())
            """)
            if 'position' in response:  # 检查返回数据结构[3](@ref)
                robot_status.update(
                    x=response['position'].get('x', 0),
                    y=response['position'].get('y', 0),
                    z=response['position'].get('z', 0)
                )
            if position_label:
                position_label.text = f"Position: ({robot_status['x']}, {robot_status['y']}, {robot_status['z']})"
        except Exception as e:
            print("Status update error:", e)
        await asyncio.sleep(1)


async def move_robot(direction: str):
    try:
        response = await ui.run_javascript(f"""
            fetch('{API_BASE}/api/move', {{
                method: 'POST',
                headers: {{ 'Content-Type': 'application/json' }},
                body: JSON.stringify({{ direction: '{direction}' }})
            }}).then(r => r.json())
        """)
        ui.notify(
            f"Moved {direction} to position ({response.position.x}, {response.position.y}, {response.position.z})")
    except Exception as e:
        ui.notify(f"移动失败: {str(e)}", type='negative')


# 启动应用
ui.run(
    title="Robot Control Panel",
    port=3000,
    storage_secret="111",
    dark=False,
    host='0.0.0.0'
)